package app

import (
	"fmt"
	"github.com/spf13/cobra"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"kubectl-view-node-taints/pkg/kubeclient"
	"kubectl-view-node-taints/pkg/printers"
	"kubectl-view-node-taints/pkg/types"
	"os"
	"time"
)


var RootCmd = &cobra.Command{
	Use:   "view-node-taints",
	Short: "查看污点信息",
	Long: `查看kubernetes节点污点信息`,
	Run: func(cmd *cobra.Command, args []string) {
		client := kubeclient.NewClient()
		run(client)
	},
}

//Execute将所有子命令添加到根命令并适当设置标志。这由main.main()调用。它只需要发生一次对rootCmd。
func Execute()  {
	if err := RootCmd.Execute();err != nil{
		fmt.Println(err)
		os.Exit(1)
	}
}



func run(client kubeclient.Visit)  {
	nodeList ,err := client.ListNode()
	if err != nil{
		panic(err)
	}
	podList ,err := client.ListPod()
	if err != nil{
		panic(err)
	}
	taintsColumnDefinitions := getTaintsColumnDefinitions(nodeList,podList)
	printTaintsColumnDefinitions(taintsColumnDefinitions)
}
func printTaintsColumnDefinitions(taintsColumnDefinitions []types.TaintsColumnDefinitions) {
	printers.Output(taintsColumnDefinitions)
}

func getTaintsColumnDefinitions(nodeList *v1.NodeList,podList *v1.PodList)[]types.TaintsColumnDefinitions{

	var taintsColumnDefinitions []types.TaintsColumnDefinitions
	for _,node := range nodeList.Items{
		status := NodeNotReady
		for _,condition := range node.Status.Conditions{
			if condition.Type == NodeReady && condition.Status == "True"{
				status = NodeReady
			}
		}

		taintsList := "<none>"
		if len(node.Spec.Taints) != 0{
			taintsList = visitTaints(node.Spec.Taints)
		}

		if node.Spec.Unschedulable {
			status += "," + NodeSchedulingDisabled
		}

		nodeAge := convertToAge(node.CreationTimestamp)

		var count int
		var cpuRequest,memRequest,cpuLimit,memLimit int64
		//根据节点name获取节点上运行的pod数量
		for _,pod := range podList.Items{
			if pod.Spec.NodeName == node.Name {
				if pod.Status.Phase != "Failed" && pod.Status.Phase != "Succeeded"{
					count ++
				}

				for i,_ := range pod.Spec.Containers{
					cpuRequest += pod.Spec.Containers[i].Resources.Requests.Cpu().MilliValue()
					memRequest += pod.Spec.Containers[i].Resources.Requests.Memory().MilliValue() /1024/1024/1000
					cpuLimit += pod.Spec.Containers[i].Resources.Limits.Cpu().MilliValue()
					memLimit += pod.Spec.Containers[i].Resources.Limits.Memory().MilliValue()  /1024/1024/1000
				}
			}
		}
		taintsColumnDefinitions = append(taintsColumnDefinitions,types.TaintsColumnDefinitions{
			Name: node.Name,
			Status: status,
			Age: nodeAge,
			Version: node.Status.NodeInfo.KernelVersion,
			Taints: taintsList,
			CpuRequests: fmt.Sprintf("%dm (%d%%)",cpuRequest,cpuRequest*100/node.Status.Allocatable.Cpu().MilliValue()),
			CpuLimits: fmt.Sprintf("%dm (%d%%)",cpuLimit,cpuLimit*100/node.Status.Allocatable.Cpu().MilliValue()),
			MemRequests: fmt.Sprintf("%dMi (%d%%)",memRequest,memRequest*100/(node.Status.Allocatable.Memory().MilliValue()/1024/1024/1000)),
			MemLimits: fmt.Sprintf("%dMi (%d%%)",memLimit,memLimit*100/(node.Status.Allocatable.Memory().MilliValue()/1024/1024/1000)),
			UseTotalPod: fmt.Sprintf("%d/%s",count,node.Status.Allocatable.Pods().String()),
		})
	}
	return taintsColumnDefinitions
}


func visitTaints(taints []v1.Taint)string  {
	var taintsStr string
	var ct int
	taintsLen := len(taints)
	for _,taint := range taints{
		taintsStr += fmt.Sprintf("%v=%v:%v",taint.Key,taint.Value,taint.Effect)
		ct += 1
		if ct < taintsLen{
			taintsStr += ","
		}
	}
	return taintsStr
}

func convertToAge(creationTimestamp metav1.Time)string  {
	s := creationTimestamp.Time.Format("2006-01-02 15:04:05Z")
	t,_ := time.Parse("2006-01-02 15:04:05Z",s)

	now := time.Now()
	sub := now.Sub(t)

	subHours := sub.Hours()
	if subHours < float64(24){
		return fmt.Sprintf("%vh", subHours)
	}

	hours := int(subHours)
	days := hours/24
	if (hours % 24)>0{
		days += 1
	}
	return fmt.Sprintf("%vd", days)
}